﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

using BizElements.TemplateEngine;

namespace BizElements.Generator
{
    public partial class TemplateVariablesCtl : UserControl
    {
        Dictionary<TemplateVariableInfo, Control> bindings = new Dictionary<TemplateVariableInfo, Control>();
        ITemplate template;
        bool isControlLoaded;
        List<Pair<Control, string>> controlsAndInitialValues = new List<Pair<Control, string>>();

        public TemplateVariablesCtl()
        {
            InitializeComponent();
        }

        private void TemplateVariablesCtl_Load(object sender, EventArgs e)
        {
            this.isControlLoaded = true;
            foreach (Pair<Control, string> ctlAndValue in this.controlsAndInitialValues)
                SetControlValue(ctlAndValue.Name, ctlAndValue.Value);
        }

        #region Properties, delegates.

        [Browsable(false)]
        public ITemplate Template
        {
            get { return this.template; }
            set { SetNewTemplateIfChanged(value); }
        }

        IVariableValueBus variableBus;

        [Browsable(false)]
        public IVariableValueBus VariableBus
        {
            get { return this.variableBus; }
            set
            {
                if (this.variableBus != value)
                {
                    this.variableBus = value;
                    if (this.variableBus != null)
                        this.variableBus.VariableValuePushed += new EventHandler<EventArgs<Pair<string, string>>>(OnVariablePushed);
                }
            }
        }        

        #endregion

        private void SetNewTemplateIfChanged(ITemplate value)
        {
            bool hasChanged;
            if ((value == null) || (this.Template == null))
                hasChanged = true;
            else
                hasChanged = (this.Template.XmlContent != value.XmlContent);

            if (hasChanged)
            {
                this.template = value;
                RefreshVariables();
                if (this.template != null)
                {
                    foreach (KeyValuePair<string, TemplateVariableInfo> var in this.template.Variables)
                        var.Value.ValueChanged += new EventHandler(VariableValue_ValueChanged);
                }
            }
        }

        void VariableValue_ValueChanged(object sender, EventArgs e)
        {
            TemplateVariableInfo var = (TemplateVariableInfo)sender;
            if (this.variableBus != null)
                this.variableBus.PushVariableValue(var.Name, var.Value);
        }

        private void RefreshVariables()
        {
            this.Controls.Clear();
            if (this.template == null)
                return;

            CreateAllVariableControlsAndSetDefaults();
            Application.DoEvents();
        }

        private string GetDefaultValue(TemplateVariableInfo variable)
        {
            string defaultValue = null;
            if (this.VariableBus != null)
                defaultValue = this.VariableBus.PullVariableValue(variable.Name);

            if (string.IsNullOrEmpty(defaultValue))
                defaultValue = variable.DefaultValue;

            return defaultValue;
        }

        private void OnVariablePushed(object sender, EventArgs<Pair<string, string>> e)
        {
            if (this.Template == null)
                return;

            TemplateVariableInfo variable;
            this.Template.Variables.TryGetValue(e.Item.Name, out variable);
            if (variable == null)
                return;

            Control ctl;
            this.bindings.TryGetValue(variable, out ctl);
            if (ctl == null)
                return;

            string currval = GetControlValue(ctl);
            bool currvalIsNullOrDefault = string.IsNullOrEmpty(currval) || (currval == variable.DefaultValue);
            if (currvalIsNullOrDefault)
            {
                variable.Value = e.Item.Value;
                if (this.isControlLoaded)
                    SetControlValue(ctl, e.Item.Value);
                else
                    this.controlsAndInitialValues.Add(new Pair<Control, string>(ctl, e.Item.Value));
            }
        }

        #region Create controls.

        const int RowGap = 4;
        const int ControlGap = 4;
        int nextRowLocation = RowGap;

        private void CreateAllVariableControlsAndSetDefaults()
        {
            this.nextRowLocation = RowGap;
            int labelWidth = 5 + Convert.ToInt32(GetMaxVariableLabelWidth());
            foreach (KeyValuePair<string, TemplateVariableInfo> var in this.Template.Variables)
            {
                Control editControl = CreateVariableControls(var.Value, labelWidth);
                this.bindings.Add(var.Value, editControl);
            }
        }                

        private Control CreateVariableControls(TemplateVariableInfo var, int labelWidth)
        {
            Label lbl = CreateLabelForVariable(var, labelWidth);
            lbl.Location = new Point(0, nextRowLocation);
            this.Controls.Add(lbl);

            Control ctl = CreateBoundControl(var, lbl);
            this.Controls.Add(ctl);
            if (this.isControlLoaded)
                SetControlValue(ctl, GetDefaultValue(var));
            else
                this.controlsAndInitialValues.Add(new Pair<Control, string>(ctl, GetDefaultValue(var)));

            this.nextRowLocation += lbl.Size.Height + RowGap;
            return ctl;
        }

        private Control CreateBoundControl(TemplateVariableInfo var, Label lbl)
        {
            Control ctl;
            if (var.AllowedValues != null && var.AllowedValues.Length > 0)
                ctl = CreateComboBoxForVariable(var);
            else
                ctl = CreateTextBoxForVariable(var);

            ctl.Location = new Point(lbl.Width + ControlGap, nextRowLocation);
            const int avgScrollBarWidth = 20;
            ctl.Width = this.Width - ctl.Location.X - 2 * ControlGap - avgScrollBarWidth;
            return ctl;
        }

        private Label CreateLabelForVariable(TemplateVariableInfo var, int labelWidth)
        {
            Label lbl = new Label();
            lbl.Width = labelWidth;
            lbl.Text = var.Description ?? var.Name;
            lbl.AutoEllipsis = true;
            this.uiToolTip.SetToolTip(lbl, var.Description);
            return lbl;
        }

        private TextBox CreateTextBoxForVariable(TemplateVariableInfo var)
        {
            TextBox txt = new TextBox();
            txt.Anchor = AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top;
            this.uiToolTip.SetToolTip(txt, var.Description);
            txt.Leave += new EventHandler(OnTextVariableChanged);
            return txt;
        }

        void OnTextVariableChanged(object sender, EventArgs e)
        {
            TextBox txt = (TextBox)sender;
            string newValue = GetControlValue(txt);
            var variable = GetVariableBoundToCtl(txt);
            // Could be null if the control has not been boutn to control yet.
            if (variable != null)
                variable.Value = newValue;
        }

        private TemplateVariableInfo GetVariableBoundToCtl(Control ctl)
        {
            foreach (KeyValuePair<TemplateVariableInfo, Control> varAndCtl in this.bindings)
            {
                if (varAndCtl.Value == ctl)
                    return varAndCtl.Key;
            }

            return null;
        }

        private ComboBox CreateComboBoxForVariable(TemplateVariableInfo var)
        {
            ComboBox ddl = new ComboBox();
            ddl.ValueMember = "Value";
            ddl.DisplayMember = "Name";            
            this.uiToolTip.SetToolTip(ddl, var.Description);
            ddl.SelectedIndexChanged += new EventHandler(OnDdlVariableChanged);
            ddl.DataSource = var.AllowedValues;
            ddl.DropDownStyle = ComboBoxStyle.DropDownList;
            return ddl;
        }

        void OnDdlVariableChanged(object sender, EventArgs e)
        {
            ComboBox ddl = (ComboBox)sender;
            string newValue = GetControlValue(ddl);
            var variable = GetVariableBoundToCtl(ddl);
            // Could be null if the control has not been bound to control yet.
            if (variable != null)
                variable.Value = newValue;
        }

        private string GetLongestVariableDescription()
        {
            string longestDescription = "";
            foreach (KeyValuePair<string, TemplateVariableInfo> var in this.Template.Variables)
            {
                string varDescription = var.Value.Description ?? var.Value.Name;
                if (varDescription.Length > longestDescription.Length)
                    longestDescription = varDescription;
            }

            return longestDescription;
        }

        private float GetMaxVariableLabelWidth()
        {
            string longestDescription = GetLongestVariableDescription();
            Graphics gfx = CreateGraphics();
            return gfx.MeasureString(longestDescription, this.Font).Width;
        }

        #endregion

        #region Get/set control value.

        private void SetControlValue(Control ctl, string value)
        {
            ComboBox ddl = ctl as ComboBox;
            if (ddl != null)
            {
                try  { ddl.SelectedValue = value; }
                catch { ddl.SelectedIndex = 0; }
            }
            else
            {
                ctl.Text = value;
            }
        }

        private string GetControlValue(Control ctl)
        {
            ComboBox ddl = ctl as ComboBox;
            if (ddl != null)
                return Convert.ToString(ddl.SelectedValue);
            else
                return ctl.Text;
        }

        #endregion

        #region Settings.

        public void ReadSettings(string keyPrefix, BizElementsSettings settings)
        {
            foreach (KeyValuePair<TemplateVariableInfo, Control> varAndControl in this.bindings)
            {
                string settingsKey = keyPrefix + "_" + varAndControl.Key.Name;
                if (!string.IsNullOrEmpty(settings[settingsKey]))
                {
                    string value = settings[settingsKey];

                    var variable = varAndControl.Key;
                    variable.Value = value;

                    Control ctl = varAndControl.Value;                    
                    if (this.isControlLoaded)
                        SetControlValue(ctl, value);
                    else
                        this.controlsAndInitialValues.Add(new Pair<Control, string>(ctl, value));
                }
            }
        }

        public void WriteSettings(string keyPrefix, BizElementsSettings settings)
        {
            foreach (KeyValuePair<TemplateVariableInfo, Control> varAndControl in this.bindings)
            {   
                var ctl = varAndControl.Value;
                string value = GetControlValue(ctl);
                
                var variable = varAndControl.Key;
                variable.Value = value;

                string settingsKey = keyPrefix + "_" + varAndControl.Key.Name;
                settings[settingsKey] = value;
            }
        }

        #endregion        
    }
}
